package com.security.config.security.authentication.sms;

import com.security.constant.RedisConstant;
import com.security.domain.po.Account;
import com.security.exception.CaptchaNotFoundException;
import com.security.service.impl.AccountServiceImpl;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.StringUtils;

/**
 * 短信验证码认证逻辑
 * 实现 AuthenticationProvider 接口，重写 authenticate 方法
 * 当类型为 SmsAuthenticationToken 的认证实体进入时才走此 Provider
 * 认证成功后返回 SmsAuthenticationToken 类型的认证实体
 *
 * @author DblSun
 */
public class SmsAuthenticationProvider implements AuthenticationProvider {
    private final AccountServiceImpl accountService;
    private final StringRedisTemplate stringRedisTemplate;

    public SmsAuthenticationProvider(AccountServiceImpl accountService,
                                     StringRedisTemplate stringRedisTemplate) {
        this.accountService = accountService;
        this.stringRedisTemplate = stringRedisTemplate;
    }


    /**
     * 验证中国手机号的方法
     *
     * @param phone 待验证的手机号
     * @return 如果是有效的中国手机号返回 true，否则返回 false
     */
    public static boolean isValidChinesePhoneNumber(String phone) {
        if (!StringUtils.hasText(phone)) {
            return false;
        }
        // 正则表达式验证中国手机号
        String regex = "^1[3-9]\\d{9}$";
        return phone.matches(regex);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsAuthenticationToken smsAuthenticationToken = (SmsAuthenticationToken) authentication;
        // 判断手机号是否合法
        if (!isValidChinesePhoneNumber(smsAuthenticationToken.getPrincipal().toString())) {
            throw new InternalAuthenticationServiceException("手机号格式错误");
        }
        Account account = (Account) accountService
                .loadUserByPhone(smsAuthenticationToken.getPrincipal().toString());
        // 判断用户是否被冻结
        if (!account.isEnabled() || !account.isAccountNonLocked() || !account.isAccountNonExpired()) {
            throw new LockedException("账号已被禁用");
        }
        boolean isExist = stringRedisTemplate
                .opsForHash().hasKey(RedisConstant.ApplicationName + RedisConstant.USER_CAPTCHA, account.getPhone());
        // 判断 redis 中验证码是否过期
        if (!isExist) {
            throw new CaptchaNotFoundException("验证码已过期");
        } else {
            String redisCaptcha = (String) stringRedisTemplate
                    .opsForHash().get(RedisConstant.ApplicationName + RedisConstant.USER_CAPTCHA, account.getPhone());
            // 判断验证码是否正确
            if (!StringUtils.hasText(redisCaptcha) || !redisCaptcha.equals(smsAuthenticationToken.getCredentials().toString())) {
                throw new BadCredentialsException("验证码错误");
            } else if (redisCaptcha.equals(smsAuthenticationToken.getCredentials().toString())) {
                // 删除验证码
                stringRedisTemplate
                        .opsForHash().delete(RedisConstant.ApplicationName + RedisConstant.USER_CAPTCHA, account.getPhone());
            }
        }
        return new SmsAuthenticationToken(account, account.getPassword(), account.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
